home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / web / noweb / src / c / finduses.nw (.txt) < prev    next >
LaTeX Document  |  1995-02-24  |  5KB  |  141 lines

  1. \section{Scanning for uses of identifiers}
  2. \subsection{Main program}
  3. <<*>>=
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <ctype.h>
  8. #include "errors.h"
  9. #include "match.h"
  10. #include "getline.h"
  11. #include "recognize.h"
  12. These choices of alphanumerics and symbols seem to work for most languages.
  13. Making [[@]] alphanumeric helps {\LaTeX}, and making [[#]]
  14. alphanumeric helps avoid false hits on C preprocessor directives like
  15. [[#define]] and [[#include]].
  16. <<*>>=
  17. static Recognizer nwindex;
  18. #define ALPHANUM "_'@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#"
  19. #define SYMBOLS "!%^&*-+:=|~<>./?`"
  20. /* note $ and \ both delimiters */
  21. @ %def ALPHANUM SYMBOLS
  22. By default, find uses within quoted code ([[[[...]]]]).
  23. <<*>>=
  24. static int showquotes = 1;
  25. <<typedefs>>
  26. <<local prototypes>>
  27. <<*>>=
  28. main(int argc, char **argv) {
  29.     FILE *fp;
  30.     char *myself = *argv;
  31.     int i;
  32.     for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != 0; i++)
  33.         if (!strcmp(argv[i], "-noquote"))
  34.             showquotes = 0;
  35.         else
  36.             errormsg(Error, "%s: unknown option -%c\n", myself, argv[i][1]);
  37.     nwindex = new_recognizer(ALPHANUM, SYMBOLS);
  38.     if (i == argc) {
  39.        <<add uses to stdin, grabbing defns from stdin>>
  40.     } else {
  41.        <<read identifiers to be defined from files named in [[argv]]>>
  42.        stop_adding(nwindex);
  43.        add_use_markers(stdin, stdout);
  44.     }
  45.     exit(errorlevel);
  46.     return errorlevel;        /* slay warning */
  47. <<read identifiers to be defined from files named in [[argv]]>>=
  48. for (; i < argc; i++)
  49.     if ((fp=fopen(argv[i],"r"))==NULL)
  50.         errormsg(Error, "%s: couldn't open file %s\n", myself, argv[i]);
  51.     else {
  52.         read_ids(fp);
  53.         fclose(fp);
  54.     }
  55. <<local prototypes>>=
  56. static void read_ids(FILE *in);
  57. <<*>>=
  58. static void read_ids(FILE *in) {
  59.     char *line;
  60.     while ((line = getline(in)) != NULL) {
  61.         if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
  62.         add_ident(nwindex, line);
  63.     }
  64. @ %def read_ids
  65. <<add uses to stdin, grabbing defns from stdin>>=
  66. {   FILE *tmp = tmpfile();
  67.     char *line;
  68.     if (tmp == NULL) <<complain about opening temp file and exit>>
  69.     while ((line = getline(stdin)) != NULL) {
  70.         if (fputs(line, tmp) == EOF) <<complain about writing temp file and exit>>
  71.         if (is_index(line, "defn")) {
  72.             if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
  73.             add_ident(nwindex, line+1+5+1+4+1);
  74.         } else if (is_index(line, "localdefn")) {
  75.             if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
  76.             add_ident(nwindex, line+1+5+1+9+1);
  77.         }
  78.     }
  79.     rewind(tmp);
  80.     stop_adding(nwindex);
  81.     add_use_markers(tmp, stdout);
  82. <<typedefs>>=
  83. typedef struct line_and_outfile {
  84.     char *line;
  85.     FILE *out;
  86. } LineOut;
  87. <<local prototypes>>=
  88. static void add_use_markers(FILE *in, FILE *out);
  89. <<*>>=
  90. static void add_use_markers(FILE *in, FILE *out) {
  91.     char *line;
  92.     int incode = 0;
  93.     LineOut info; info.line = (char *)0; info.out = out;
  94.     while ((line = getline(in)) != NULL) {
  95.         if (is_begin(line, "code") || showquotes && is_keyword(line, "quote"))
  96.         incode = 1;
  97.     else if (is_end(line, "code") || is_keyword(line, "endquote"))
  98.         incode = 0;
  99.     if (is_keyword(line, "text") && incode) {
  100.         info.line = line + 6; /* skip "@text " */
  101.             search_for_ident(nwindex, line, write_index_use, &info);
  102.             if (*info.line && *info.line != '\n') 
  103.         fprintf(out, "@text %s", info.line);    /* has newline */
  104.     } else
  105.         fprintf(out, "%s", line);
  106.     }        
  107. @ %def add_use_markers
  108. We gradually cut out the uses, and the tail of the line is left in [[info.line]],
  109. to be printed by the code above.
  110. There's a tricky bug lurking here---if one identifier is a prefix of another,
  111. but both are recognized (as with the C$++$ [[::]] separator), we have to avoid
  112. writing them both out in full, because that would duplicate text unnecessarily.
  113. As a result, we always emit the line in pieces.
  114. The function [[emit_up_to(f, s, limit)]] emits the piece of the string [[s]] up to 
  115. but not including [[limit]], if any.
  116. It returns [[limit]] or [[s]], whichever is greater.
  117. <<*>>=
  118. static void write_index_use(void *closure, char *id, char *instance) {
  119.   LineOut *info = (LineOut *) closure;
  120.   info->line = emit_up_to(info->out, info->line, instance);
  121.   fprintf(info->out, "@index use %s\n", id);
  122.   info->line = emit_up_to(info->out, info->line, instance + strlen(id));
  123. @ %def write_index_use
  124. <<*>>=
  125. static char *emit_up_to(FILE *f, char *s, char *limit) {
  126.   if (s < limit) {
  127.     char saved = *limit;
  128.     *limit = 0;
  129.     fprintf(f, "@text %s\n", s);
  130.     *limit = saved;
  131.     return limit;
  132.   } else {
  133.     return s;
  134. <<local prototypes>>=
  135. static void write_index_use(void *closure, char *id, char *instance);
  136. static char *emit_up_to(FILE *f, char *s, char *limit);
  137. <<complain about opening temp file and exit>>=
  138. errormsg(Fatal, "%s: couldn't open temporary file\n");
  139. <<complain about writing temp file and exit>>=
  140. errormsg(Fatal, "%s: error writing temporary file\n");
  141.